/**
 * JungoMobileWeb
 * ProductController.java
 * 
 * Copyright 2013 Teachizen
 * Written by cloudstick
 *	2013. 7. 26. first written.
 *
 * note:
 * 		session check를 AOP로 변경해야 함.
 */
package com.jungo.web.controller;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import com.jungo.web.bean.Deal;
import com.jungo.web.bean.Favorite;
import com.jungo.web.bean.Product;
import com.jungo.web.bean.User;
import com.jungo.web.dao.DealDao;
import com.jungo.web.dao.ProductDao;
import com.jungo.web.service.DealService;
import com.jungo.web.util.ThumbnailUtil;

/**
 * @author cloudstick
 * 
 */
@Controller
@Slf4j
public class ProductController {
	
	enum ProductStatus {
		NONE(0),
		REGISTERED(1),
		REVIEW(11),
		CONFIRM(21),
		DEALING(31),
		COMPLETION(41),
		REJECT(51);
		
		int value;
		
		private ProductStatus(int value) {
			this.value = value;
		}
		
		public int getValue() {
			return value;
		}
	}
	
	private static final String MULTIPART_SEPARATOR = "multipart/";
	
	@Autowired
	private ProductDao productDao;
	
	@Autowired
	private DealDao dealDao;
	
	@Autowired
	private DealService dealService;
	
	@Autowired
	private MessageSourceAccessor messageSourceAccessor;
	
	@RequestMapping(value="/products.do", method=RequestMethod.GET)
	public String products(ModelMap model) {
		List<Product> products = productDao.getProducts();
		
		for(Product product : products) {
			product.setImage_path(getImageVirtualPath(product.getImage_path()));
		}
		log.info("Product list[{}]", products);
		
		model.addAttribute("products", products);
		return "product/products";
	}
	
	/**
	 * 모든 상품 정보를 가져와서 카테고리 별로 화면에 표시
	 * @param session 로그인된 사용자를 확인하기 위한 세션
	 * @param model 상품 리스트 전체를 view로 전달
	 * @return products.jsp
	 */
	@RequestMapping(value="/products.do", method=RequestMethod.POST)
	public String products(HttpSession session, ModelMap model) {
		
		if ( session.getAttribute("user") == null ) {
			log.info("sesion null.");
			return "redirect:/index.do";
		}
		
		return products(model);
	}
	
	@RequestMapping(value="/product.do", method=RequestMethod.GET)
	public String product(@RequestParam("pid") int pid, HttpServletRequest request,
						ModelMap model) {
		
		User user = (User)request.getSession().getAttribute("user");
		if ( user == null ) {
			return "redirect:/login.do";
		}
		
		if ( pid <= 0 ) {
			throw new RuntimeException("Product id is invalid.");
		}
		
		Product product = productDao.getProduct(pid);
		product.setImage_path(getImageVirtualPath(product.getImage_path()));
		model.addAttribute("product", product);
		
		log.debug("user[{}], product[{}]", user, product);
		if ( user != null && user.getUserid() != null ) {
			Favorite favorite = productDao.getFavoriteByProduct(product);
			if ( favorite != null ) {
					if ( user.getUserid().equals(favorite.getUserid()) ) {
					model.addAttribute("dealButton", false);
					return "product/product";
				}
			}
			if ( !user.getUserid().equals(product.getUserid()) ) {
				return "product/product";
			}
			model.addAttribute("favorite", favorite);
		}
		
		return "product/product";
	}
	
	/**
	 * 단일 상품의 상세 정보를 표시
	 * @param session 로그인된 사용자를 확인하기 위한 세션
	 * @param pid 단일 상품의 아이디
	 * @param model 단일 상품 상세 정보
	 * @return product.jsp
	 */
	@RequestMapping(value="/product.do", method=RequestMethod.POST)
	public String product(HttpSession session, 
								@RequestParam("pid") int pid,
								@RequestParam("fid") int fid,
								@RequestParam("submitValue") String submitValue,
								@RequestParam("fromPage") String fromPage,
								ModelMap model) {
		
		if ( session.getAttribute("user") == null ) {
			return "redirect:/login.do";
		}
		
		if ( pid <= 0 ) {
			throw new RuntimeException("Product id is invalid.");
		}
		
		Product product = productDao.getProduct(pid);
		product.setPid(pid);
		
		Favorite favorite = new Favorite();
		favorite.setFid(fid);
		favorite.setStatus(21);
		
		Deal deal = new Deal();
		deal.setFid(fid);
		deal.setPid(pid);
		if ( "complete".equals(submitValue) ) {
			log.debug("Called complete.");
			product.setStatus(41);
			productDao.updateProductStatus(product);
			deal.setStatus(21);
			productDao.updateFavoriteStatus(favorite);
			dealDao.updateDealStatus(deal);
		} else if ( "cancel".equals(submitValue) ) {
			log.debug("Called cancel.");
			product.setStatus(21);
			productDao.updateProductStatus(product);
			deal.setStatus(31);
			productDao.updateFavoriteStatus(favorite);
			dealDao.updateDealStatus(deal);
			dealService.deal(product);
		}
		
		return "redirect:/product.do?pid=" + pid + "&fromPage=" + fromPage;
	}
	
	@RequestMapping(value="/registerFavorite.do", method=RequestMethod.GET)
	public String registerFavorite(HttpSession session) {
		
		if ( session.getAttribute("user") == null ) {
			return "redirect:/index.do";
		}
		
		return "product/registerFavorite";
	}
	
	/**
	 * 관심 상품을 등록한다.
	 * @param session session 로그인된 사용자를 확인하기 위한 세션
	 * @param favorite 관심 상품 객체
	 * @return products.do
	 */
	@RequestMapping(value="/registerFavorite.do", method=RequestMethod.POST)
	public String registerFavorite(HttpSession session, 
									@ModelAttribute Favorite favorite,
									ModelMap model) {
		
		User user = (User)session.getAttribute("user");
		if ( user == null ) {
			return "redirect:/index.do";
		}
		
		nonNull(favorite).setUserid(user.getUserid());
		try {
			favorite.setTitle(URLDecoder.decode(favorite.getTitle(), "UTF-8"));
			favorite.setContact(URLDecoder.decode(favorite.getContact(), "UTF-8"));
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException("Exception occured whiling decode title.", e);
		}
		favorite.setFid(productDao.getFavoriteId());
		productDao.setFavorite(favorite);
		
		model.addAttribute("fromPage", "favorite");
		List<Product> matchedProducts = dealService.deal(favorite);
		if ( matchedProducts == null || matchedProducts.isEmpty() ) {
			model.addAttribute("dealCode", "noMatchedProduct");
			return "product/deal";
		}
		favorite.setStatus(11);
		productDao.updateFavoriteStatus(favorite);
		
		model.addAttribute("matchedProducts", matchedProducts);
		model.addAttribute("dealCode", "valid");
		return "product/deal";
	}
	
	/**
	 * 상품 등록 페이지
	 * @param request 요청 객체
	 * @param session session 로그인된 사용자를 확인하기 위한 세션
	 * @return register.jsp
	 */
	@RequestMapping(value="/register.do", method=RequestMethod.GET)
	public String register(HttpServletRequest request, HttpSession session) {
		
		if ( session.getAttribute("user") == null ) {
			return "redirect:/login.do";
		}
		
		return "product/register";
	}
	
	/**
	 * 상품 등록 정보를 받아 저장 후 상품 리스트 출력
	 * @param request 이미지 정보 등이 포함된 요청 객체
	 * @param session session 로그인된 사용자를 확인하기 위한 세션
	 * @return products.do로 리다이렉트
	 */
	@RequestMapping(value="/register.do", method=RequestMethod.POST)
	public String register(MultipartHttpServletRequest request,
							HttpSession session) {
		
		User user = (User)session.getAttribute("user");
		if ( user == null ) {
			return "redirect:/login.do";
		}
		
		//contentType을 확인하여 'multipart'가 아니면 예외 발생
		String contentType = request.getContentType();
		if ( contentType == null || !contentType.toLowerCase().startsWith(MULTIPART_SEPARATOR) ) {
			throw new RuntimeException("Invalid request.");
		}
		
		String productTitle = "";
		String productDescription = "";
		try {
			//js에서 encodeURIComponent로
			//인코딩하여 전송한다. 따라서 서버에서는 이를 디코딩하여 사용한다.
			productTitle = URLDecoder.decode(request.getParameter("title"), "UTF-8");
			productDescription = URLDecoder.decode(request.getParameter("description"), "UTF-8");
		} catch (UnsupportedEncodingException e) {
			log.error("Exception occured.", e);
			throw new RuntimeException("Invalid character encoding.", e);
		}
		
		int pid = productDao.getProductId();
		Product product = new Product();
		product.setPid(pid);
		product.setCategory(request.getParameter("category"));
		product.setTitle(productTitle);
		product.setDescription(productDescription);
		product.setPrice(Integer.parseInt(request.getParameter("price")));
		product.setUserid(user.getUserid());
		product.setStatus(ProductStatus.CONFIRM.getValue());
	
		MultipartFile multiFile = request.getFile("image");
		product.setImage_path(uploadImage(multiFile, pid));
		productDao.setProduct(product);
		
		dealService.deal(product);
		
		return "redirect:/products.do";
	}
	
	/**
	 * multiFile 이미지를 pathBase에 상품 아이디별로 저장
	 * 실제 경로를 통해 파일을 저장하고 상대 경로를 DB에 저장한다.
	 * @param multiFile 사용자가 등록한 이미지
	 * @param pathBase 기본 이미지 경로
	 * @param pid 상품 아이디
	 * @return 이미지 경로
	 */
	private String uploadImage(MultipartFile multiFile, int pid) {
		
		if ( pid <= 0 ) {
			throw new RuntimeException("Invalid product id[" + pid + "]");
		}
		
		String imagePath = getImagePath(pid, nonNull(multiFile).getOriginalFilename());
		String realPath = getImageRealPath(imagePath);
		log.debug("path[{}]", realPath);
		makeDirs(realPath);
		try {
			@Cleanup FileOutputStream out = new FileOutputStream(realPath);
			out.write(multiFile.getBytes());
			out.flush();
			/*
			makeThumbnail(realPath, getImageThumbnailPath(imagePath), 100, 80);
			makeThumbnail(realPath, getImageBigThumbnailPath(imagePath), 200, 160);
			*/
			return imagePath;
		} catch (IOException e) {
			log.error("Exception occured.", e);
			throw new RuntimeException("Failed to create image file.", e);
		}
	}
	
	private void makeThumbnail(String realPath, String thumbnailPath, int width, int height) {
		makeDirs(thumbnailPath);
		ThumbnailUtil.makeThumbnail(realPath, thumbnailPath, width, height);
	}
	
	/**
	 * path의 부모 디렉토리를 생성
	 * @param path 파일이름이 포함된 경로
	 */
	private void makeDirs (String path) {
		int indexSlash = nonNull(path).lastIndexOf("/");
		int indexBackSlash = path.lastIndexOf("\\");
		int index = Math.max(indexSlash, indexBackSlash);
		if(index > 0) {
			String dirs = path.substring(0, index);
			File dir = new File(dirs);
			if(dir.exists()) { return; }
			
			if ( !dir.mkdirs() ) {
				throw new RuntimeException("Unable to create directories for " + dirs);
			}
		}
	}
	
	private String getImagePath(int pid, String name) {
		return pid + File.separator + name;
	}
	
	private String getImageRealPath(String imagePath) {
		return getMessage("RESOURCE.IMAGE.PATH.REAL.BASE") + imagePath;
	}
	
	private String getImageVirtualPath(String imagePath) {
		return getMessage("RESOURCE.IMAGE.PATH.VIRTUAL.BASE") + imagePath;
	}
	
	private String getImageThumbnailPath(String imagePath) {
		return getMessage("RESOURCE.IMAGE.PATH.THUMBNAIL_BASE") + imagePath;
	}
	
	private String getImageBigThumbnailPath(String imagePath) {
		return getMessage("RESOURCE.IMAGE.PATH.BIG.THUMBNAIL_BASE") + imagePath;
	}
	
	private String getImageBigVirtualPath(String imagePath) {
		return getMessage("RESOURCE.IMAGE.PATH.BIG.VIRTUAL.BASE") + imagePath;
	}
	
	private String getMessage(String code) {
		return messageSourceAccessor.getMessage(code);
	}
	
	static <T> T nonNull(T argument) {
		if ( argument == null ) {
			throw new IllegalArgumentException("argument cannot be null.");
		}
		return argument;
	}
	
	@RequestMapping("deal.do")
	public String dealWith(@RequestParam int pid, 
							HttpSession session, 
							ModelMap model) {
		
		User user = (User)session.getAttribute("user");
		if ( user == null ) {
			model.addAttribute("from", "dealWith");
			model.addAttribute("pid", pid);
			return "redirect:/login.do";
		}
		
		model.addAttribute("fromPage", "products");
		Product product = productDao.getProduct(pid);
		if ( user.getUserid().equals(product.getUserid()) ) {
			model.addAttribute("dealCode", "invalid");
			return "product/deal";
		}
		Favorite favorite = new Favorite();
		favorite.setFid(productDao.getFavoriteId());
		favorite.setCategory(product.getCategory());
		favorite.setContact(user.getMobile());
		favorite.setMax_price(product.getPrice());
		favorite.setMin_price(product.getPrice());
		favorite.setTitle(product.getTitle());
		favorite.setUserid(user.getUserid());
		
		productDao.setFavorite(favorite);
		try {
			dealService.dealWithOne(favorite);
		} catch(Exception e) {
			model.addAttribute("dealCode", "duplicated");
			return "product/deal";
		}
		
		product.setImage_path(getImageVirtualPath(product.getImage_path()));
		List<Product> matchedProducts = new ArrayList<Product>();
		matchedProducts.add(product);
		model.addAttribute("product", matchedProducts);
		model.addAttribute("dealCode", "valid");
		return "product/deal";
	}
	
	@RequestMapping("myFavorites.do")
	public String myFavorites(HttpSession session, ModelMap model) {
		User user = (User)session.getAttribute("user");
		if ( user == null ) {
			model.addAttribute("from", "myFavorites");
			return "redirect:/login.do";
		}
		
		List<Favorite> favorites = productDao.getFavoritesByUser(user.getUserid());
		log.debug("userid[{}]'s favorites[{}]", user.getUserid(), favorites);
		
		model.addAttribute("favorites", favorites);
		return "product/my_favorites";
	}
	
	@RequestMapping("myProducts.do")
	public String myProducts(HttpSession session, ModelMap model) {
		User user = (User)session.getAttribute("user");
		log.debug("myProducts.do user[{}]", user);
		if ( user == null ) {
			model.addAttribute("from", "myProducts");
			return "redirect:/login.do";
		}
		
		List<Product> products = productDao.getProductsByUser(user.getUserid());
		
		for(Product product : products) {
			product.setImage_path(getImageVirtualPath(product.getImage_path()));
		}
		log.info("Product list[{}]", products);
		
		model.addAttribute("products", products);
		return "product/my_products";
	}
	
	@RequestMapping("favorite.do")
	public String favorite(HttpSession session,
							@RequestParam("fid") int fid,
							ModelMap model) {
		User user = (User)session.getAttribute("user");
		if ( user == null ) {
			model.addAttribute("from", "myFavorites");
			return "redirect:/login.do";
		}
		
		Product product = dealDao.getMatchedProductByFavorite(fid);
		if ( product != null ) {
			model.addAttribute("product", product);
		}
		
		Favorite favorite = productDao.getFavorite(fid);
		DateFormat df = new SimpleDateFormat("yyyy년 MM월 dd일(HH시 mm분)");
		favorite.setStr_dt(df.format(favorite.getReg_dt()));
		
		model.addAttribute("favorite", favorite);
		return "product/favorite";
	}
}
